/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_httpd.htm
 *
 * Zhiqim Httpd is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.httpd;

import java.util.List;

import org.zhiqim.kernel.model.maps.HashMapSO;
import org.zhiqim.kernel.model.maps.HashMapSS;
import org.zhiqim.kernel.model.maps.MapSV;
import org.zhiqim.kernel.json.Jsons;
import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.kernel.util.DateTimes;
import org.zhiqim.kernel.util.Ints;
import org.zhiqim.kernel.util.Longs;
import org.zhiqim.kernel.util.Strings;

/**
 * HttpSession抽象类，公共属性
 * 1.sessionId 会话唯一标识,用于SessionManager管理
 * 2.created 会话创建时间
 * 3.accessed 会话访问时间
 * 4.sessionName 会话名称,用于业务上作为唯一标识
 * 5.sessionUser 会话对象,用于业务对象模型,作为存储
 * 6.attributes 属性列表
 * 持久化时,6个值都将保存到持久化层
 *
 * @version v1.0.0 @author zouzhigang 2020-5-14 新建与整理
 */
public abstract class HttpSessionAbs implements HttpSession
{
    //会话管理器
    protected transient HttpContext context;
    protected transient HttpSessionManager sessionManager;
    
    //定义浸入式更换sessionId的保留老编号，在HttpResponse时清理，因此是临时保存
    protected transient String oldSessionId;
    
    //会话标准六属性，编号、两个时间、两个客户端属性
    protected String sessionId;
    protected long created;
    protected long accessed;
    protected String userIp;
    protected String userAgent;

    //会话业务属性表，由业务侧设置和管理
    protected HashMapSO attributeMap;
    protected HashMapSS attributeGenerics;
    
    //会话用户属性，从业务属性表中独立出来，特指登录用户属性，简化开发
    protected HttpSessionUser sessionUser;
    
    /******************************************************************************************/
    //构造函数
    /******************************************************************************************/
    
    /** 空构造函数用于远程时创建再解析 */
    public HttpSessionAbs()
    {
    }
    
    /** 参数构造函数用于本地创建传到远程 */
    public HttpSessionAbs(String sessionId, String userIp, String userAgent, HttpSessionManager sessionManager)
    {
        this.sessionManager = sessionManager;
        this.context = sessionManager.getContext();
        
        this.sessionId = sessionId;
        this.created = System.currentTimeMillis();
        this.accessed = created;
        
        this.userIp = userIp;
        this.userAgent = userAgent;
        
        this.attributeMap = new HashMapSO();
        this.attributeGenerics = new HashMapSS();
    }

    @Override /** 子类必须使用的同步到远程的方法 */
    public abstract void flush();
    
    /******************************************************************************************/
    //用于业务浸入式更改sessionId（注：谨慎使用），HttpResponse会oldSessionId进行处理
    /******************************************************************************************/
    
    @Override /** 用于业务修改ID值，请谨慎使用 */
    public void setNewSessionId(String newSessionId)
    {
        if (newSessionId.equals(this.sessionId))
            return;
        
        this.oldSessionId = this.sessionId;
        this.sessionId = newSessionId;
    }
    
    
    @Override /** 获取老会话编号，用于响应时查出来从管理器中删除 */
    public String getOldSessionId()
    {
        return oldSessionId;
    }
    
    @Override /** 清除老会话编号，在响应时从管理器中删除老会话后，清除新会话中的老会话标志 */
    public void clearOldSessionId()
    {
        this.oldSessionId = null;
    }
    
    /******************************************************************************************/
    //使会话失效
    /******************************************************************************************/
    
    @Override /** 会话主动失效 */
    public void invalidate()
    {
        if (sessionUser != null)
        {//解除绑定
            sessionUser.setSession(null);
            sessionUser = null;
        }
        
        if (attributeMap != null)
        {//清除属性
            attributeMap.clear();
            attributeMap = null;
        }
        
        if (oldSessionId != null)
        {//清除旧会话
            sessionManager.invalidateSession(oldSessionId);
            oldSessionId = null;
        }
        
        if (sessionId != null)
        {//清除当前会话
            sessionManager.invalidateSession(sessionId);
            sessionId = null;
        }
    }
    
    /******************************************************************************************/
    //获取参数判断值
    /******************************************************************************************/
    
    @Override
    public HttpContext getContext()
    {
        return context;
    }
    
    @Override
    public String getId()
    {
        return sessionId;
    }
    
    @Override
    public boolean isNew()
    {
        return created == accessed;
    }
    
    @Override
    public String getUserIp()
    {
        return userIp;
    }
    
    @Override
    public String getUserAgent()
    {
        return this.userAgent;
    }
    
    @Override
    public int getMaxInactiveInterval()
    {
        return sessionManager.getSessionTimeout();
    }
    
    @Override
    public long getCreated()
    {
        return created;
    }
    
    @Override
    public String getCreateTime()
    {
        return DateTimes.toDateTimeString(created);
    }
    
    @Override
    public long getLastAccessed()
    {
        return accessed;
    }
    
    @Override
    public String getLastAccessTime()
    {
        return DateTimes.toDateTimeString(accessed);
    }
    
    @Override
    public long getRemainSecond()
    {
        long remain = System.currentTimeMillis() - getLastAccessed();
        return getMaxInactiveInterval() - remain / 1000;
    }
    
    @Override
    public long getRemainMinute()
    {
        return getRemainSecond() / 60;
    }
    
    @Override
    public long[] getRemainTime()
    {
        long time = getRemainSecond();
        long day = time / 86400;
        long hour = time % 86400 / 3600;
        long minute = time % 3600 / 60;
        long second = time % 60;
        
        return new long[]{day, hour, minute, second};
    }
    
    @Override
    public String getRemainTimeDesc()
    {
        long[] times = getRemainTime();
        
        StringBuilder strb = new StringBuilder();
        if (times[0] > 0)
            strb.append(times[0]).append("天");
        if (times[1] > 0)
            strb.append(times[1]).append("小时");
        if (times[2] > 0)
            strb.append(times[2]).append("分");
        strb.append(times[3]).append("秒");
        
        return strb.toString();
    }
    
    /**********************************************/
    //SessionUser相关
    /**********************************************/    

    @Override /** 绑定会话用户信息 */
    public boolean bindSessionUser(HttpSessionUser sessionUser)
    {
        boolean binded = false;
        if (this.sessionUser != null && !this.sessionUser.equals(sessionUser))
        {//清理原关系
            this.sessionUser.setSession(null);
            this.sessionUser = null;
            binded = true;
        }
        
        if (sessionUser != null)
        {//增加新关系
            this.sessionUser = sessionUser;
            this.sessionUser.setSession(this);
            binded = true;
        }
        
        return binded;
    }

    @Override /** 解绑会话用户信息 */
    public boolean unbindSessionUser()
    {
        if (this.sessionUser == null)
            return false;
        
        this.sessionUser.setSession(null);
        this.sessionUser = null;
        
        return true;
    }
    
    /** 子类会话在远程重建时回调设置会话和会话用户的关联 */
    protected void bindSessionUser(HttpSession session)
    {
        if (sessionUser == null)
            return;
        
        sessionUser.setSession(session);
    }
    
    /**********************************************/
    //获取或判断SessionUser相关
    /**********************************************/    
    
    @Override /** 判断会话中是否有用户信息 */
    public boolean hasSessionUser()
    {
        return sessionUser != null;
    }
    
    @Override /** 获取会话用户信息中用户类，多个时抛异常，多会话用户时请谨慎调用 */
    public HttpSessionUser getSessionUser()
    {
        return sessionUser;
    }
    
    @Override /** 判断会话中是否有用户信息且指定的用户类 */
    public <T extends HttpSessionUser> boolean hasSessionUser(Class<T> clazz)
    {
        return sessionUser != null && sessionUser.getClass() == clazz;
    }
    
    @Override /** 获取会话用户信息且指定的用户类 */
    @SuppressWarnings("unchecked")
    public <T extends HttpSessionUser> T getSessionUser(Class<T> clazz)
    {
        return sessionUser == null?null:sessionUser.getClass() == clazz?(T)sessionUser:null;
    }
    
    @Override /** 获取会话用户名称，多个时抛异常，多会话用户时请谨慎调用 */
    public String getSessionName()
    {
        return sessionUser == null?null:sessionUser.getSessionName();
    }
    
    /**********************************************/
    //写入关联到session中attribute列表
    /**********************************************/
    
    @Override
    public Object removeAttribute(String name)
    {
        attributeGenerics.remove(name);
        return attributeMap.remove(name);
    }
    
    @Override
    public void setAttribute(String name, Object value)
    {
        Asserts.as(!(value instanceof List || value instanceof MapSV)?null:"本方法不支持List<T>和MapSV<V>，请使用有泛型参数的setAttribute方法");
        
        attributeMap.put(name, value);
    }
    
    @Override
    public void setAttribute(String name, Object value, Class<?> genericClass)
    {
        Asserts.as((value instanceof List || value instanceof MapSV)?null:"本方法仅支持List<T>和MapSV<V>，请使用无泛型参数的setAttribute方法");
        
        attributeMap.put(name, value);
        attributeGenerics.put(name, genericClass.getName());
    }
    
    /**********************************************/
    //获取关联到session中attribute列表
    /**********************************************/
    
    @Override
    public HashMapSO getAttributes()
    {
        return attributeMap;
    }
    
    @Override
    public boolean hasAttribute(String name)
    {
        return attributeMap.containsKey(name);
    }
    
    @Override
    public Object getAttribute(String name)
    {
        return attributeMap.get(name);
    }
    
    @Override
    public Object getAttribute(String name, Object defaultValue)
    {
        Object value = attributeMap.get(name);
        return (value == null)?defaultValue:value;
    }
    
    @Override
    public String getAttributeString(String name)
    {
        return getAttributeString(name, null);
    }
    
    @Override
    public String getAttributeString(String name, String defaultValue)
    {
        Object value = getAttribute(name);
        return (value == null)?defaultValue:Strings.trim(String.valueOf(value));
    }
    
    @Override
    public int getAttributeInt(String name)
    {
        return getAttributeInt(name, -1);
    }
    
    @Override
    public int getAttributeInt(String name, int defaultValue)
    {
        return Ints.toInt(attributeMap.get(name), defaultValue);
    }
    
    @Override
    public long getAttributeLong(String name)
    {
        return getAttributeLong(name, -1);
    }
    
    @Override
    public long getAttributeLong(String name, long defaultValue)
    {
        return Longs.toLong(attributeMap.get(name), defaultValue);
    }
    
    @Override
    public boolean getAttributeBoolean(String name, boolean defaultValue)
    {
        Object value = attributeMap.get(name);
        if (value == null)
            return defaultValue;
        else if (value instanceof Boolean)
            return (Boolean)value;
        else if (value instanceof String)
            return Boolean.parseBoolean((String)value);
        else
            return defaultValue;
    }
    
    /*******************************************************************/
    //会话用户不公开的moreMap方法，通过透传让RmiSession可访问
    /*******************************************************************/
    
    protected void removeSessionUserMore(String name)
    {
        sessionUser.removeMore(name);
    }
    
    protected void addSessionUserMore(String name, String value)
    {
        sessionUser.addMore(name, value);
    }
    
    protected HashMapSS getSessionUserMoreMap()
    {
        return sessionUser.getMoreMap();
    }
    
    protected void setSessionUserValueGeneric(String key, Object value, String genericClassName)
    {
        sessionUser.setValueGeneric(key, value, genericClassName);
    }
    
    protected String getSessionUserValueGeneric(String key)
    {
        return sessionUser.getValueGeneric(key);
    }
    
    /**********************************************/
    //清理和转换成字符串
    /**********************************************/
    
    @Override
    public String toString()
    {
        return Jsons.toString(this);
    }
}
